/**
 * fshows.com Copyright (C) 2013-2020 All Rights Reserved.
 */
package com.fshows.leshuapay.sdk.client.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fshows.leshuapay.sdk.client.LeshuaMerchantGateWayClient;
import com.fshows.leshuapay.sdk.exception.LeshuaException;
import com.fshows.leshuapay.sdk.request.LeshuaBizRequest;
import com.fshows.leshuapay.sdk.response.LeshuaBaseResponse;
import com.fshows.leshuapay.sdk.util.ReqSerialNoUtil;
import com.fshows.leshuapay.sdk.util.SignUtil;
import com.fshows.leshuapay.sdk.util.ValidateUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

/**
 * @author zhaoyi
 * @version LeshuaMerchantGateWayClientImpl.java, v 0.1 2020-07-23 11:40 AM zhaoyi
 */
@Slf4j
@Data
public class LeshuaMerchantGateWayClientImpl implements LeshuaMerchantGateWayClient {

  /**
   * 从connectManager获取Connection 超时时间
   */
  private static final int CONNECTION_REQUEST_TIMEOUT = 5000;

  /**
   * 读取数据的超时时间
   */
  private static final int SOCKET_TIMEOUT = 10000;

  /**
   * 连接超时时间
   */
  private static final int CONNECT_TIMEOUT = 6000;

  /**
   * 商户服务地址
   */
  private String serverUrl;

  /**
   * 服务商编号
   */
  private String agentId;

  /**
   * 服务商私钥
   */
  private String privateKey;

  /**
   * 请求超时时间，单位毫秒
   */
  private Integer timeout;

  /**
   * @param serverUrl  商户地址
   * @param agentId    服务商编号
   * @param privateKey 服务商私钥
   * @param timeout    毫秒
   */
  public LeshuaMerchantGateWayClientImpl(
          String serverUrl, String agentId, String privateKey, Integer timeout) {
    this.serverUrl = serverUrl;
    this.agentId = agentId;
    this.privateKey = privateKey;
    this.timeout = timeout;
  }

  @Override
  public <T> LeshuaBaseResponse<T> execute(LeshuaBizRequest<T> request, String url, Integer timeout, String subAgentId)
          throws LeshuaException {
    ValidateUtil.validateWithThrow(request);
    // 获取签名值
    String sign = SignUtil.getMerchantSign(request, privateKey);
    RequestConfig requestConfig = null;
    if (timeout == null || timeout <= 0) {
      requestConfig =
              RequestConfig.custom()
                      .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                      .setSocketTimeout(SOCKET_TIMEOUT)
                      .setConnectTimeout(CONNECT_TIMEOUT)
                      .build();
    } else {
      requestConfig =
              RequestConfig.custom()
                      .setConnectionRequestTimeout(timeout)
                      .setSocketTimeout(timeout)
                      .setConnectTimeout(timeout)
                      .build();
    }
    HttpPost httppost = new HttpPost(serverUrl + url);
    httppost.setConfig(requestConfig);
    MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
    multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    multipartEntityBuilder.setCharset(Charset.forName(SignUtil.DEFAULT_CHARSET));
    String reqSerialNo = ReqSerialNoUtil.getReqSerialNo();
    // 代理商编号
    multipartEntityBuilder.addPart(
            "agentId", new StringBody(StrUtil.blankToDefault(subAgentId, agentId), ContentType.APPLICATION_JSON));
    // 版本
    multipartEntityBuilder.addPart(
            "version", new StringBody(SignUtil.DEFAULT_VERSION, ContentType.APPLICATION_JSON));
    // 请求流水号
    multipartEntityBuilder.addPart(
            "reqSerialNo", new StringBody(reqSerialNo, ContentType.APPLICATION_JSON));
    // 数据内容
    multipartEntityBuilder.addPart(
            "data", new StringBody(JSON.toJSONString(request), ContentType.APPLICATION_JSON));
    // 签名值
    multipartEntityBuilder.addPart("sign", new StringBody(sign, ContentType.APPLICATION_JSON));
    HttpEntity reqEntity = multipartEntityBuilder.build();
    httppost.setEntity(reqEntity);
    CloseableHttpResponse httpResponse = null;
    HttpEntity resEntity = null;
    CloseableHttpClient httpClient = null;
    try {
      httpClient = HttpClients.createDefault();
      log.info("方法名:{},请求乐刷接口入参:{}", url, JSON.toJSONString(request));
      httpResponse = httpClient.execute(httppost);
      resEntity = httpResponse.getEntity();
      String responseStr = EntityUtils.toString(resEntity, SignUtil.DEFAULT_CHARSET);
      LeshuaBaseResponse<T> response = new LeshuaBaseResponse<>();
      JSONObject jsonObject = JSON.parseObject(responseStr);
      response.setReqSerialNo(jsonObject.getString("reqSerialNo"));
      response.setRespCode(jsonObject.getString("respCode"));
      response.setRespMsg(jsonObject.getString("respMsg"));
      response.setVersion(jsonObject.getString("version"));
      response.setData(JSON.parseObject(jsonObject.getString("data"), request.getResponseClass()));
      log.info("方法名:{},请求乐刷接口返回参数:{}", url, response);
      return response;
    } catch (Exception e) {
      throw new LeshuaException("调用失败", e);
    } finally {
      try {
        EntityUtils.consume(resEntity);
      } catch (IOException e) {
        log.error("释放HttpEntity出错，错误信息：" + e.getMessage(), e);
      }
      if (httpResponse != null) {
        try {
          httpResponse.close();
        } catch (IOException e) {
          log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
        }
      }
    }
  }

  @Override
  public <T> LeshuaBaseResponse<List<T>> executeList(
          LeshuaBizRequest<T> request, String url, Integer timeout) throws LeshuaException {
    ValidateUtil.validateWithThrow(request);
    // 获取签名值
    String sign = SignUtil.getMerchantSign(request, privateKey);
    RequestConfig requestConfig = null;
    if (timeout == null || timeout <= 0) {
      requestConfig =
              RequestConfig.custom()
                      .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                      .setSocketTimeout(SOCKET_TIMEOUT)
                      .setConnectTimeout(CONNECT_TIMEOUT)
                      .build();
    } else {
      requestConfig =
              RequestConfig.custom()
                      .setConnectionRequestTimeout(timeout)
                      .setSocketTimeout(timeout)
                      .setConnectTimeout(timeout)
                      .build();
    }
    HttpPost httppost = new HttpPost(serverUrl + url);
    httppost.setConfig(requestConfig);
    MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
    multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    multipartEntityBuilder.setCharset(Charset.forName(SignUtil.DEFAULT_CHARSET));
    String reqSerialNo = ReqSerialNoUtil.getReqSerialNo();
    // 代理商编号
    multipartEntityBuilder.addPart(
            "agentId", new StringBody(agentId, ContentType.APPLICATION_JSON));
    // 版本
    multipartEntityBuilder.addPart(
            "version", new StringBody(SignUtil.DEFAULT_VERSION, ContentType.APPLICATION_JSON));
    // 请求流水号
    multipartEntityBuilder.addPart(
            "reqSerialNo", new StringBody(reqSerialNo, ContentType.APPLICATION_JSON));
    // 数据内容
    multipartEntityBuilder.addPart(
            "data", new StringBody(JSON.toJSONString(request), ContentType.APPLICATION_JSON));
    // 签名值
    multipartEntityBuilder.addPart("sign", new StringBody(sign, ContentType.APPLICATION_JSON));
    HttpEntity reqEntity = multipartEntityBuilder.build();
    httppost.setEntity(reqEntity);
    CloseableHttpResponse httpResponse = null;
    HttpEntity resEntity = null;
    CloseableHttpClient httpClient = null;
    try {
      httpClient = HttpClients.createDefault();
      log.info("请求乐刷接口入参:{}", JSON.toJSONString(request));
      httpResponse = httpClient.execute(httppost);
      resEntity = httpResponse.getEntity();
      String responseStr = EntityUtils.toString(resEntity, SignUtil.DEFAULT_CHARSET);
      LeshuaBaseResponse<List<T>> response = new LeshuaBaseResponse<>();
      JSONObject jsonObject = JSON.parseObject(responseStr);
      response.setReqSerialNo(jsonObject.getString("reqSerialNo"));
      response.setRespCode(jsonObject.getString("respCode"));
      response.setRespMsg(jsonObject.getString("respMsg"));
      response.setVersion(jsonObject.getString("version"));
      response.setData(JSON.parseArray(jsonObject.getString("data"), request.getResponseClass()));
      log.info("请求乐刷接口返回参数:{}", response);
      return response;
    } catch (Exception e) {
      throw new LeshuaException("调用失败", e);
    } finally {
      try {
        EntityUtils.consume(resEntity);
      } catch (IOException e) {
        log.error("释放HttpEntity出错，错误信息：" + e.getMessage(), e);
      }
      if (httpResponse != null) {
        try {
          httpResponse.close();
        } catch (IOException e) {
          log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
        }
      }
    }
  }

  /**
   * 调用商户相关请求
   *
   * @param request    商户入参
   * @param url        实际业务请求地址
   * @param timeout    超时设置，单位毫秒
   * @param subAgentId 二代编号. 为空时，则默认使用主代编号
   * @return
   * @throws LeshuaException
   */
  @Override
  public LeshuaBaseResponse<String> executeString(LeshuaBizRequest request, String url, Integer timeout, String subAgentId) throws LeshuaException {
    ValidateUtil.validateWithThrow(request);
    // 获取签名值
    String sign = SignUtil.getMerchantSign(request, privateKey);
    RequestConfig requestConfig = null;
    if (timeout == null || timeout <= 0) {
      requestConfig =
              RequestConfig.custom()
                      .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                      .setSocketTimeout(SOCKET_TIMEOUT)
                      .setConnectTimeout(CONNECT_TIMEOUT)
                      .build();
    } else {
      requestConfig =
              RequestConfig.custom()
                      .setConnectionRequestTimeout(timeout)
                      .setSocketTimeout(timeout)
                      .setConnectTimeout(timeout)
                      .build();
    }
    HttpPost httppost = new HttpPost(serverUrl + url);
    httppost.setConfig(requestConfig);
    MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
    multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    multipartEntityBuilder.setCharset(Charset.forName(SignUtil.DEFAULT_CHARSET));
    String reqSerialNo = ReqSerialNoUtil.getReqSerialNo();
    // 代理商编号
    multipartEntityBuilder.addPart(
            "agentId", new StringBody(StrUtil.blankToDefault(subAgentId, agentId), ContentType.APPLICATION_JSON));
    // 版本
    multipartEntityBuilder.addPart(
            "version", new StringBody(SignUtil.DEFAULT_VERSION, ContentType.APPLICATION_JSON));
    // 请求流水号
    multipartEntityBuilder.addPart(
            "reqSerialNo", new StringBody(reqSerialNo, ContentType.APPLICATION_JSON));
    // 数据内容
    multipartEntityBuilder.addPart(
            "data", new StringBody(JSON.toJSONString(request), ContentType.APPLICATION_JSON));
    // 签名值
    multipartEntityBuilder.addPart("sign", new StringBody(sign, ContentType.APPLICATION_JSON));
    HttpEntity reqEntity = multipartEntityBuilder.build();
    httppost.setEntity(reqEntity);
    CloseableHttpResponse httpResponse = null;
    HttpEntity resEntity = null;
    CloseableHttpClient httpClient = null;
    try {
      httpClient = HttpClients.createDefault();
      log.info("方法名:{},请求乐刷接口入参:{}", url, JSON.toJSONString(request));
      httpResponse = httpClient.execute(httppost);
      resEntity = httpResponse.getEntity();
      String responseStr = EntityUtils.toString(resEntity, SignUtil.DEFAULT_CHARSET);
      LeshuaBaseResponse<String> response = new LeshuaBaseResponse<>();
      JSONObject jsonObject = JSON.parseObject(responseStr);
      response.setReqSerialNo(jsonObject.getString("reqSerialNo"));
      response.setRespCode(jsonObject.getString("respCode"));
      response.setRespMsg(jsonObject.getString("respMsg"));
      response.setVersion(jsonObject.getString("version"));
      response.setData(jsonObject.getString("data"));
      log.info("方法名:{},请求乐刷接口返回参数:{}", url, response);
      return response;
    } catch (Exception e) {
      throw new LeshuaException("调用失败", e);
    } finally {
      try {
        EntityUtils.consume(resEntity);
      } catch (IOException e) {
        log.error("释放HttpEntity出错，错误信息：" + e.getMessage(), e);
      }
      if (httpResponse != null) {
        try {
          httpResponse.close();
        } catch (IOException e) {
          log.error("关闭HttpResponse出错，错误信息：" + e.getMessage(), e);
        }
      }
    }
  }
}
